/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// A filter that breaks up an AC3 audio elementary stream into frames
// Implementation

#include "include/AC3AudioStreamFramer.hh"
#include "StreamParser.hh"
#include "../groupsock/include/GroupsockHelper.hh"

////////// AC3AudioStreamParser definition //////////

class AC3FrameParams {
public:
    AC3FrameParams() : samplingFreq(0) {}

    // 8-byte header at the start of each frame:
    //  u_int32_t hdr0, hdr1;
    unsigned hdr0, hdr1;

    // parameters derived from the headers
    unsigned kbps, samplingFreq, frameSize;

    void setParamsFromHeader();
};

class AC3AudioStreamParser : public StreamParser {
public:
    AC3AudioStreamParser(AC3AudioStreamFramer *usingSource,
                         FramedSource *inputSource);

    virtual ~AC3AudioStreamParser();

public:
    void testStreamCode(unsigned char ourStreamCode,
                        unsigned char *ptr, unsigned size);

    unsigned parseFrame(unsigned &numTruncatedBytes);
    // returns the size of the frame that was acquired, or 0 if none was

    void registerReadInterest(unsigned char *to, unsigned maxSize);

    AC3FrameParams const &currentFrame() const { return fCurrentFrame; }

    Boolean haveParsedAFrame() const { return fHaveParsedAFrame; }

    void readAndSaveAFrame();

private:
    static void afterGettingSavedFrame(void *clientData, unsigned frameSize,
                                       unsigned numTruncatedBytes,
                                       struct timeval presentationTime,
                                       unsigned durationInMicroseconds);

    void afterGettingSavedFrame1(unsigned frameSize);

    static void onSavedFrameClosure(void *clientData);

    void onSavedFrameClosure1();

private:
    AC3AudioStreamFramer *fUsingSource;
    unsigned char *fTo;
    unsigned fMaxSize;

    Boolean fHaveParsedAFrame;
    unsigned char *fSavedFrame;
    unsigned fSavedFrameSize;
    char fSavedFrameFlag;

    // Parameters of the most recently read frame:
    AC3FrameParams fCurrentFrame;
};


////////// AC3AudioStreamFramer implementation //////////

AC3AudioStreamFramer::AC3AudioStreamFramer(UsageEnvironment &env,
                                           FramedSource *inputSource,
                                           unsigned char streamCode)
        : FramedFilter(env, inputSource), fOurStreamCode(streamCode) {
    // Use the current wallclock time as the initial 'presentation time':
    gettimeofday(&fNextFramePresentationTime, NULL);

    fParser = new AC3AudioStreamParser(this, inputSource);
}

AC3AudioStreamFramer::~AC3AudioStreamFramer() {
    delete fParser;
}

AC3AudioStreamFramer *
AC3AudioStreamFramer::createNew(UsageEnvironment &env,
                                FramedSource *inputSource,
                                unsigned char streamCode) {
    // Need to add source type checking here???  #####
    return new AC3AudioStreamFramer(env, inputSource, streamCode);
}

unsigned AC3AudioStreamFramer::samplingRate() {
    if (!fParser->haveParsedAFrame()) {
        // Because we haven't yet parsed a frame, we don't yet know the input
        // stream's sampling rate.  So, we first need to read a frame
        // (into a special buffer that we keep around for later use).
        fParser->readAndSaveAFrame();
    }

    return fParser->currentFrame().samplingFreq;
}

void AC3AudioStreamFramer::flushInput() {
    fParser->flushInput();
}

void AC3AudioStreamFramer::doGetNextFrame() {
    fParser->registerReadInterest(fTo, fMaxSize);
    parseNextFrame();
}

#define MILLION 1000000

struct timeval AC3AudioStreamFramer::currentFramePlayTime() const {
    AC3FrameParams const &fr = fParser->currentFrame();
    unsigned const numSamples = 1536;
    unsigned const freq = fr.samplingFreq;

    // result is numSamples/freq
    unsigned const uSeconds = (freq == 0) ? 0
                                          : ((numSamples * 2 * MILLION) / freq + 1) /
                                            2; // rounds to nearest integer

    struct timeval result;
    result.tv_sec = uSeconds / MILLION;
    result.tv_usec = uSeconds % MILLION;
    return result;
}

void AC3AudioStreamFramer
::handleNewData(void *clientData, unsigned char *ptr, unsigned size,
                struct timeval /*presentationTime*/) {
    AC3AudioStreamFramer *framer = (AC3AudioStreamFramer *) clientData;
    framer->handleNewData(ptr, size);
}

void AC3AudioStreamFramer
::handleNewData(unsigned char *ptr, unsigned size) {
    fParser->testStreamCode(fOurStreamCode, ptr, size);

    parseNextFrame();
}

void AC3AudioStreamFramer::parseNextFrame() {
    unsigned acquiredFrameSize = fParser->parseFrame(fNumTruncatedBytes);
    if (acquiredFrameSize > 0) {
        // We were able to acquire a frame from the input.
        // It has already been copied to the reader's space.
        fFrameSize = acquiredFrameSize;

        // Also set the presentation time, and increment it for next time,
        // based on the length of this frame:
        fPresentationTime = fNextFramePresentationTime;

        struct timeval framePlayTime = currentFramePlayTime();
        fDurationInMicroseconds = framePlayTime.tv_sec * MILLION + framePlayTime.tv_usec;
        fNextFramePresentationTime.tv_usec += framePlayTime.tv_usec;
        fNextFramePresentationTime.tv_sec
                += framePlayTime.tv_sec + fNextFramePresentationTime.tv_usec / MILLION;
        fNextFramePresentationTime.tv_usec %= MILLION;

        // Call our own 'after getting' function.  Because we're not a 'leaf'
        // source, we can call this directly, without risking infinite recursion.
        afterGetting(this);
    } else {
        // We were unable to parse a complete frame from the input, because:
        // - we had to read more data from the source stream, or
        // - the source stream has ended.
    }
}


////////// AC3AudioStreamParser implementation //////////

static int const kbpsTable[] = {32, 40, 48, 56, 64, 80, 96, 112,
                                128, 160, 192, 224, 256, 320, 384, 448,
                                512, 576, 640};

void AC3FrameParams::setParamsFromHeader() {
    unsigned char byte4 = hdr1 >> 24;

    unsigned char kbpsIndex = (byte4 & 0x3E) >> 1;
    if (kbpsIndex > 18) kbpsIndex = 18;
    kbps = kbpsTable[kbpsIndex];

    unsigned char samplingFreqIndex = (byte4 & 0xC0) >> 6;
    switch (samplingFreqIndex) {
        case 0:
            samplingFreq = 48000;
            frameSize = 4 * kbps;
            break;
        case 1:
            samplingFreq = 44100;
            frameSize = 2 * (320 * kbps / 147 + (byte4 & 1));
            break;
        case 2:
        case 3: // not legal?
            samplingFreq = 32000;
            frameSize = 6 * kbps;
    }
}

AC3AudioStreamParser
::AC3AudioStreamParser(AC3AudioStreamFramer *usingSource,
                       FramedSource *inputSource)
        : StreamParser(inputSource, FramedSource::handleClosure, usingSource,
                       &AC3AudioStreamFramer::handleNewData, usingSource),
          fUsingSource(usingSource), fHaveParsedAFrame(False),
          fSavedFrame(NULL), fSavedFrameSize(0) {
}

AC3AudioStreamParser::~AC3AudioStreamParser() {
}

void AC3AudioStreamParser::registerReadInterest(unsigned char *to,
                                                unsigned maxSize) {
    fTo = to;
    fMaxSize = maxSize;
}

void AC3AudioStreamParser
::testStreamCode(unsigned char ourStreamCode,
                 unsigned char *ptr, unsigned size) {
    if (ourStreamCode == 0)
        return; // we assume that there's no stream code at the beginning of the data

    if (size < 4) return;
    unsigned char streamCode = *ptr;

    if (streamCode == ourStreamCode) {
        // Remove the first 4 bytes from the stream:
        memmove(ptr, ptr + 4, size - 4);
        totNumValidBytes() = totNumValidBytes() - 4;
    } else {
        // Discard all of the data that was just read:
        totNumValidBytes() = totNumValidBytes() - size;
    }
}

unsigned AC3AudioStreamParser::parseFrame(unsigned &numTruncatedBytes) {
    if (fSavedFrameSize > 0) {
        // We've already read and parsed a frame.  Use it instead:
        memmove(fTo, fSavedFrame, fSavedFrameSize);
        delete[] fSavedFrame;
        fSavedFrame = NULL;
        unsigned frameSize = fSavedFrameSize;
        fSavedFrameSize = 0;
        return frameSize;
    }

    try {
        saveParserState();

        // We expect an AC3 audio header (first 2 bytes == 0x0B77) at the start:
        while (1) {
            unsigned next4Bytes = test4Bytes();
            if (next4Bytes >> 16 == 0x0B77) break;
            skipBytes(1);
            saveParserState();
        }
        fCurrentFrame.hdr0 = get4Bytes();
        fCurrentFrame.hdr1 = test4Bytes();

        fCurrentFrame.setParamsFromHeader();
        fHaveParsedAFrame = True;

        // Copy the frame to the requested destination:
        unsigned frameSize = fCurrentFrame.frameSize;
        if (frameSize > fMaxSize) {
            numTruncatedBytes = frameSize - fMaxSize;
            frameSize = fMaxSize;
        } else {
            numTruncatedBytes = 0;
        }

        fTo[0] = fCurrentFrame.hdr0 >> 24;
        fTo[1] = fCurrentFrame.hdr0 >> 16;
        fTo[2] = fCurrentFrame.hdr0 >> 8;
        fTo[3] = fCurrentFrame.hdr0;
        getBytes(&fTo[4], frameSize - 4);
        skipBytes(numTruncatedBytes);

        return frameSize;
    } catch (int /*e*/) {
#ifdef DEBUG
        fUsingSource->envir() << "AC3AudioStreamParser::parseFrame() EXCEPTION (This is normal behavior - *not* an error)\n";
#endif
        return 0;  // the parsing got interrupted
    }
}

void AC3AudioStreamParser::readAndSaveAFrame() {
    unsigned const maxAC3FrameSize = 4000;
    fSavedFrame = new unsigned char[maxAC3FrameSize];
    fSavedFrameSize = 0;

    fSavedFrameFlag = 0;
    fUsingSource->getNextFrame(fSavedFrame, maxAC3FrameSize,
                               afterGettingSavedFrame, this,
                               onSavedFrameClosure, this);
    fUsingSource->envir().taskScheduler().doEventLoop(&fSavedFrameFlag);
}

void AC3AudioStreamParser
::afterGettingSavedFrame(void *clientData, unsigned frameSize,
                         unsigned /*numTruncatedBytes*/,
                         struct timeval /*presentationTime*/,
                         unsigned /*durationInMicroseconds*/) {
    AC3AudioStreamParser *parser = (AC3AudioStreamParser *) clientData;
    parser->afterGettingSavedFrame1(frameSize);
}

void AC3AudioStreamParser
::afterGettingSavedFrame1(unsigned frameSize) {
    fSavedFrameSize = frameSize;
    fSavedFrameFlag = ~0;
}

void AC3AudioStreamParser::onSavedFrameClosure(void *clientData) {
    AC3AudioStreamParser *parser = (AC3AudioStreamParser *) clientData;
    parser->onSavedFrameClosure1();
}

void AC3AudioStreamParser::onSavedFrameClosure1() {
    delete[] fSavedFrame;
    fSavedFrame = NULL;
    fSavedFrameSize = 0;
    fSavedFrameFlag = ~0;
}
